#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__all__ = []

from collections import namedtuple
import logging

Logger = logging.getLogger('PySolFC.LayoutRegions')
canvasSize = namedtuple('CanvasSize', ('width', 'height'))
LEFT, RIGHT, BELOW, ABOVE = 1, -1, 1, -1


class RegionLayout(object):

    def _region(self, x=-999, y=-999, h=999999, w=999999):
        """
        _region(stacks:tup, x:int=-999, y:int=-999, h:int=999999,
          w:int=999999) -> tup

        Save region area for a layout stack group.

        :keyword integer x: left x coordinate.
        :keyword integer y: top y coordinate.
        :keyword integer h: right x coordinate.
        :keyword integer w: bottom y coordinate.
        :return: tuple of coordinates for each corner of rectangle.
        """
        return (x, y, w, h,)

    def _apply_group_regions(self, game):
        """
        _apply_group_regions(game:obj) -> None

        Apply saved regions of layout stack groups to matching game stacks.
        """
        for stack, rect in self._regions:
            game._set_region([game.allstacks[game.stackmap[(s.x, s.y)]]
                             for s in stack], rect)

    def _set_horz_foundations(self, x, y, ncols, bases):
        """
        _set_horz_foundations(x:int, y:int, width:int, bases:seq) -> None

        Set horz foundation group with width of cols and enough rows to apply
        all bases to a pile.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param integer ncols: num horz cols.
        :param sequence bases: Base caps to apply.
        """
        if len(bases) == 0:
            return
        for (x, y), suit in zip(self.col_block_iter(x, y, npiles=len(bases),
                                                    ncols=ncols), bases):
            self.s['foundations'].append(self._create_stack(x, y, suit=suit))

    def _set_vert_foundations(self, x, y, nrows, bases):
        """
        _set_vert_foundations(x:int, y:int, nrows:int, bases:seq) -> None

        Set vert foundation group with height of rows and enough cols to apply
        all bases to a pile.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param integer nrows: num vert rows.
        :param sequence bases: Base caps to apply.
        """
        if len(bases) == 0:
            return
        for (x_, y_), suit in zip(self.row_block_iter(x, y, npiles=len(bases),
                                                      nrows=nrows), bases):
            self.s['foundations'].append(self._create_stack(x_, y_, suit=suit))

    def _set_rows(self, x, y, rows, **kw):
        """
        _set_rows(x:int, y:int, rows:int, ncols:int,
                  [xcards:int | ycards:int]) -> None

        Set rows group with ncols width and enough height to apply num rows.
        *cards will set the step between piles, accordingly. In addition, set
        _regions for s.rows.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param integer rows: total num piles.
        :param integer ncols: num horz cols.
        :keyword integer xcards: possible horz cards.
        :keyword integer ycards: possible vert cards.

        note:: Calls _get_stack_offset which will raise AssertionError if both
          keywords set.
        """
        if rows == 0:
            return
        offset = self._get_stack_offset(**kw)
        for x_, y_ in self.col_block_iter(x, y, npiles=rows, **kw):
            self.s['rows'].append(self._create_stack(x_, y_))
            self.s['rows'][-1].set_offset(*offset)
        # Regions disabled as some games drag and drop does not work correct?
        #self._regions += [(self.s['rows'], self._region(x=x, y=y,
        #                                   w=x_ + self.XS, h=y_ + self.YS),)]

    def _set_reserves(self, x, y, reserves, **kw):
        """
        _set_rows(x:int, y:int, rows:int, ncols:int,
                  [xcards:int | ycards:int]) -> None

        Set rows group with ncols width and enough height to apply num rows.
        *cards will set the step between piles, accordingly. In addition, set
        _regions for s.rows.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param integer rows: total num piles.
        :param integer ncols: num horz cols.
        :keyword integer xcards: possible horz cards.
        :keyword integer ycards: possible vert cards.

        note:: Calls _get_stack_offset which will raise AssertionError if both
          keywords set.
        """
        if reserves == 0:
            return
        offset = self._get_stack_offset(**kw)
        for x_, y_ in self.col_block_iter(x, y, npiles=reserves, **kw):
            self.s['reserves'].append(self._create_stack(x_, y_))
            self.s['reserves'][-1].set_offset(*offset)
        # Regions disabled as some games drag and drop does not work correct?
        #self._regions += [(self.s['resreves'], self._region(x=x, y=y,
        #                                    w=x_ + self.XS, h=y_ + self.YS),)]

    def _set_horz_reserves(self, x, y, reserves, reserve_texts, anchor, **kw):
        """
        _set_horz_reserves(x:int, y:int, resereves:int, reserve_texts:bool,
                           anchor:str, [xcards:int | ycards:int]) -> None

        Set reserves group with reserves width. *cards will set the step
        between piles, accordingly. In addition, set _regions for s.reserves.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param integer reserves: total reserve piles.
        :param bool reserve_texts: set ncards canvas label.
        :param string anchor: canvas label place off pile.
        :keyword integer xcards: possible horz cards.
        :keyword integer ycards: possible vert cards.

        note:: Calls _get_stack_offset which will raise AssertionError if both
          keywords set.
        """
        if reserves == 0:
            return
        offset = self._get_stack_offset(**kw)
        for x_, y_ in self.col_block_iter(x, y, npiles=reserves,
                                          ncols=reserves, **kw):
            self.s['reserves'].append(self._create_stack(x_, y_))
            self.s['reserves'][-1].set_offset(*offset)
            if reserve_texts:
                self.s['reserves'][-1].set_ncards(anchor)
        # Regions disabled as some games drag and drop does not work correct?
        #self._regions += [(self.s['reserves'], self._region(x=x, y=y,
        #                                    w=x_ + self.XS, h=y_ + self.YS),)]

    def _set_vert_reserves(self, x, y, reserves, reserve_texts, anchor, **kw):
        """
        _set_vert_reserves(x:int, y:int, resereves:int, reserve_texts:bool,
                           anchor:str, [xcards:int | ycards:int]) -> None

        Set reserves group with reserves height. *cards will set the step
        between piles, accordingly. In addition, set _regions for s.reserves.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param integer reserves: total reserve piles.
        :param bool reserve_texts: set ncards canvas label?
        :param string anchor: canvas label place off pile.
        :keyword integer xcards: possible horz cards.
        :keyword integer ycards: possible vert cards.

        note:: Calls _get_stack_offset which will raise AssertionError if both
          keywords set.
        """
        if reserves == 0:
            return
        offset = self._get_stack_offset(**kw)
        for x_, y_ in self.row_block_iter(x, y, npiles=reserves,
                                          nrows=reserves, **kw):
            self.s['reserves'].append(self._create_stack(x_, y_))
            self.s['reserves'][-1].set_offset(*offset)
            if reserve_texts:
                self.s['reserves'][-1].set_ncards(anchor)
        # Regions disabled as some games drag and drop does not work correct?
        #self._regions += [(self.s['reserves'], self._region(x=x, y=y,
        #                                    w=x_ + self.XS, h=y_ + self.YS),)]

    def _set_horz_talon(self, x, y, anchor, align, texts, wastecards):
        """
        _set_horz_talon(x:int, y:int, anchor:str, align:int, texts:bool,
                        wastecards:int) -> None

        Set talon group with talon and optional waste when has_waste placed
        according to align horz. LEFT places right, RIGHT places left.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param string anchor: canvas label place off pile.
        :param integer align: LEFT places waste right, RIGHT places
          waste left.
        :param bool texts: set ncards canvas label?
        :param integer wastecards: possible horz cards for waste.
        """
        self.s['talon'] = self._create_stack(x, y)
        if texts:
            self.s['talon'].set_ncards(anchor)
        self.size = canvasSize(max(self.size.width, x + self.XS),
                               max(self.size.height, y + self.YS,))
        if self._has_waste:
            x += self.XS * align
            self.s['wastes'].append(self._create_stack(x, y))
            if texts:
                self.s['wastes'][-1].set_ncards(anchor)
            if wastecards > 0:
                xoffset = min(self.XOFFSET,
                             (self.size.width - 2 * self.XS) / self.NCARDS)
                self.s['wastes'][-1]._set_offset(xoffset * align, 0)
        self.size = canvasSize(max(self.size.width, x + self.XS),
                               max(self.size.height, y + self.YS,))

    def _set_vert_talon(self, x, y, anchor, align, texts, wastecards):
        """
        _set_vert_talon(x:int, y:int, anchor:str, align:int, texts:bool,
                        wastecards:int) -> None

        Set talon group with talon and optional waste when _has_waste placed
        according to align vert. LEFT places below, RIGHT places above.

        :param integer x: left x coordinate.
        :param integer y: top y coordinate.
        :param string anchor: canvas label place off pile.
        :param integer align: LEFT places waste below, RIGHT places
          waste above.
        :param bool texts: set ncards canvas label?
        :param integer wastecards: possible horz cards for waste.
        """
        self.s['talon'] = self._create_stack(x, y)
        if texts:
            self.s['talon'].set_ncards(anchor)
        self.size = canvasSize(max(self.size.width, x + self.XS),
                               max(self.size.height, y + self.YS,))
        if self._has_waste:
            y += self.YS * align
            self.s['wastes'].append(self._create_stack(x, y))
            if texts:
                self.s['wastes'][-1].set_ncards(anchor)
            if wastecards > 0:
                yoffset = min(self.XOFFSET,
                             (self.size.width - 2 * self.XS) / self.NCARDS)
                self.s['wastes'][-1]._set_offset(0, yoffset * align)
        self.size = canvasSize(max(self.size.width, x + self.XS),
                               max(self.size.height, y + self.YS,))